package com.nowcoder.community.util;

import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author Cl
 * @Date 2024-2024/4/29  16:55
 * @Description 敏感词过滤器
 */
@Component
public class SensitiveFilter {
    /**Trie 前缀树解构匹配敏感词*/
    private class TrieNode{
        private boolean isKeyWordEnd = false;
        private Map<Character , TrieNode> subNodes = new HashMap<>();

        public boolean isKeyWordEnd(){
            return isKeyWordEnd;
        }

        public void setKeyWordEnd(boolean keyWordEnd){
            isKeyWordEnd = keyWordEnd;
        }

        /**添加子节点*/
        public void addSubNode(Character c ,TrieNode node){
            subNodes.put(c,node);
        }

        /**获取子节点*/
        public TrieNode getSubNode(Character c){
            return subNodes.get(c);
        }
    }

    private static final Logger logger = LoggerFactory.getLogger(SensitiveFilter.class);
    /**敏感词替换符*/
    private static final String REPLACEMENT = "***";
    /**根节点*/
    private TrieNode root = new TrieNode();

    /**初始化方法,根据敏感词列表构建前缀树*/
    @PostConstruct
    public void init(){
        try(
                // getResourceAsStream()这个方法使到resource文件夹路径下找资源加载进来
                InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
                BufferedReader br = new BufferedReader(new InputStreamReader(is)) // 便于一行一行的读敏感词
        ) {
            String keyWord;
            while ((keyWord = br.readLine()) != null) this.addKeyWord(keyWord);
        }catch (Exception e){
            logger.error("添加敏感词文件失败:"+e.getMessage());
        }
    }

    /**将一个敏感词添加到前缀树*/
    public void addKeyWord(String keyWord){
        if(StringUtils.isBlank(keyWord)) return;
        char[] arr = keyWord.toCharArray();
        TrieNode tempNode = root;
        for (int i = 0; i < arr.length; i++) {
            TrieNode subNode = tempNode.getSubNode(arr[i]);
            if(subNode == null){
                subNode = new TrieNode();
                tempNode.addSubNode(arr[i] , subNode);
            }
            tempNode = subNode;
        }
        tempNode.setKeyWordEnd(true);
    }

    /**过滤敏感词 , 返回过滤后的字符串 , 支持跳过特殊字符*/
    public String filter(String text){
        if(StringUtils.isBlank(text)) return text;
        TrieNode tempNode = root;
        int begin = 0 , position = 0;
        StringBuilder sb = new StringBuilder();
        while (position < text.length()){
            char c = text.charAt(position);
            // 跳过特殊字符
            if(isSysbol(c)){
                if(tempNode == root){// 保留敏感词之外的特殊字符
                    sb.append(c);
                    begin++;
                }
                position++;
                continue;
            }
            tempNode = tempNode.getSubNode(c);
            if(tempNode == null){// 一轮匹配完没有发现敏感词,begin向后走又开始匹配
                sb.append(text.charAt(begin));
                position = ++begin;
                tempNode = root;
            }else if(tempNode.isKeyWordEnd()){// 发现敏感词 , begin直接走到敏感词之后又开始匹配
                sb.append(REPLACEMENT);
                begin = ++position;
                tempNode = root;
            }else {
                position++;
            }
        }
        sb.append(text.substring(begin));// 最后一轮匹配结果没有被append,手动添加一下
        return sb.toString();
    }

    /**判断是否是特殊字符 */
    private boolean isSysbol(Character c){
        return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
    }
}
